module dungeon.player;

import std.math;

import nade.time;
import nade.vector;
import nade.angle;
import nade.color;
import nade.primitive;
import nade.sprite;
import nade.random;

import dungeon.mover;
import dungeon.map;
import dungeon.map_object;
import dungeon.item;
import dungeon.equipment;
import dungeon.character;
import dungeon.game;
import dungeon.race;
import dungeon.klass;
import dungeon.attack;


class Player : CharacterObject, IIterator {
	this(Map map, const Vector2i coord, Race race, Klass klass)
	{
		super(map, new Character(map.game));
		_mover.from = _mover.to = coord;
		enterTile(coord);

		_inputDirection = Vector2i.ZERO;
		_nextAttack = curTime;

		_race = race;
		_klass = klass;
		_character.sprite = race.sprite(klass.id);

		addEffect(new HealthBar(this));
	}

	override void iterate()
	{
		void arrived(const Vector2i previous, const Vector2i current)
		{
			leaveTile(previous);

			const dest = current + _inputDirection;
			if(_inputDirection.isZero  ||  !_map.isPassable(dest))
			{
				_mover.travel = 0;
				_moving = false;

				checkPickups(current);
			}
			else
			{
				enterTile(dest);
				_mover.to = current + _inputDirection;
			}
		}

		if(_moving)
		{
			_mover.move(frameTime*2, &arrived);
		}
		else if(!_inputDirection.isZero)
		{
			const dest = _mover.from + _inputDirection;
			if(_map.isPassable(dest))
			{
				enterTile(dest);
				_moving = true;
				_mover.to = dest;
			}
		}

		foreach(click; _clicks)
		{
			if(_selection !is null) _selection.remove, _selection = null;

			foreach(occupant; _map.getTileOccupants(Vector2i(cast(int)click.x, cast(int)click.y)))
			{
				if(cast(CharacterObject)occupant !is null)
				{
					_selection = new SelectionEffect(cast(CharacterObject)occupant);
					break;
				}
			}
		}
		_clicks.length = 0;

		if(_selection !is null  &&  _nextAttack < curTime)
		{
			if(_selection.target.character.stat("HP") <= 0)
			{
				_selection.remove, _selection = null;
			}
			else
			{
				auto weapon = _character.weapon((_selection.target.position - position).modulus);
				if(weapon !is null  &&  _map.trace(position, _selection.target.position))
				{
					_nextAttack = curTime + random(weapon.attack.delayMin, weapon.attack.delayMax);
					new AttackObject(_map, weapon.attack, this, _selection.target);
				}
			}
		}
	}

	void checkPickups(const Vector2i coord)
	{
		ItemObject[] items;
		foreach(obj; _map.getTileOccupants(coord))
			if((cast(ItemObject)obj) !is null)
				items ~= cast(ItemObject)obj;

		foreach(item; items)
		{
			if(_character.inventory.insert(item.item) >= 0)
			{
				if(cast(Equipment)item.item) _character.loadout.equip(cast(Equipment)item.item);
				item.leaveTile(coord);
			}
		}
	}

	override uint zIndex() { return 10; }

	void addClick(Vector2 position) { _clicks ~= position; }

	Vector2i inputDirection() { return _inputDirection; }
	Vector2i inputDirection(const Vector2i rhs) { return _inputDirection = rhs; }

	protected {
		Vector2i			_inputDirection;
		Vector2[]			_clicks;
		SelectionEffect		_selection = null;
		real				_nextAttack;

		Race				_race;
		Klass				_klass;
	}
}


class SelectionEffect : IEffect {
	protected {
		CharacterObject		_target;
	}

	this(CharacterObject target)
	{
		_target = target;
		_target.addEffect(this);
	}

	void render()
	{
		auto angle = Radians(curTime*10);
		auto halfSize = Vector2(angle.cos*0.025 + 0.5, angle.sin*0.025 + 0.5);

		Color(1.0, 1.0, 0.75).render;
		renderEllipse(_target.position, halfSize, 32);

		Color.BLACK.render;
		renderFilledRectangle(_target.position + Vector2(-0.5, -0.4), _target.position + Vector2(0.5, -0.5));

		const portion = fmin(fmax(_target.character.stat("HP")/_target.character.statNaked("HP"), 0), 1);
		Color.RED.render;
		renderFilledRectangle(_target.position + Vector2(-0.5, -0.4), _target.position + Vector2(-0.5 + portion, -0.5));
	}

	void remove()
	{
		_target.removeEffect(this);
	}

	CharacterObject target() { return _target; }
}


class HealthBar : IEffect {
	protected {
		CharacterObject		_target;
	}

	this(CharacterObject target)
	{
		_target = target;
		_target.addEffect(this);
	}

	void render()
	{
		Color.BLACK.render;
		renderFilledRectangle(_target.position + Vector2(-0.5, -0.4), _target.position + Vector2(0.5, -0.5));

		const portion = fmin(fmax(_target.character.stat("HP")/_target.character.statNaked("HP"), 0), 1);
		Color.RED.render;
		renderFilledRectangle(_target.position + Vector2(-0.5, -0.4), _target.position + Vector2(-0.5 + portion, -0.5));
	}

	void remove()
	{
		_target.removeEffect(this);
	}

	CharacterObject target() { return _target; }
}
